package net.sourceforge.peertwitter.core.applications.communication;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.HashSet;
import java.util.Set;

import net.sourceforge.peertwitter.scamp.messaging.HeartbeatMessage;

import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.junit.Test;

import resources.LiveNodeHandle;
import rice.environment.Environment;
import rice.environment.logging.LogManager;
import rice.environment.time.TimeSource;
import rice.p2p.commonapi.Endpoint;
import rice.p2p.commonapi.Message;
import rice.p2p.commonapi.NodeHandle;
import rice.pastry.Id;
import rice.selector.SelectorManager;

public class SwarmImplTest {
	class MockedSelector extends SelectorManager {

		public MockedSelector(String instance, TimeSource timeSource,
				LogManager log) {
			super(instance, timeSource, log);
		}
		
	}
	
	private Swarm swarm;
	private NodeHandle swarmOwner;
	private Endpoint endpoint;
	private Mockery context;
	private TimeSource timeSource;
	private Set<NodeHandle> participants;
	private LiveNodeHandle aParticipant;
	private LiveNodeHandle anotherParticipant;
	private Environment environment;
	protected long currentMillis;
	protected Id swarmOwnerID;
	
	@Before
	public void createSwarm() {
		context = new Mockery() {
			{
				setImposteriser(ClassImposteriser.INSTANCE);
			}
		};
		participants = new HashSet<NodeHandle>();
		
		aParticipant = new LiveNodeHandle(Id.build("0xabcde"));
		participants.add(aParticipant);
		
		anotherParticipant = new LiveNodeHandle(Id.build("0xabcdsddse"));
		participants.add(anotherParticipant);

		swarmOwner = context.mock(NodeHandle.class);
		endpoint = context.mock(Endpoint.class);
		timeSource = context.mock(TimeSource.class);
		
		environment = context.mock(Environment.class);
		swarm = new SwarmImpl(endpoint);
		swarm.addParticipant(aParticipant);
		swarm.addParticipant(anotherParticipant);
		
		swarmOwnerID = context.mock(Id.class);
		currentMillis = System.currentTimeMillis();
		
		context.checking(new Expectations() {
			{
				allowing(endpoint).getLocalNodeHandle();
				will(returnValue(swarmOwner));
				
				allowing(endpoint).getEnvironment();
				will(returnValue(environment));
				
				allowing(environment).getTimeSource();
				will(returnValue(timeSource));
				
				allowing(timeSource).currentTimeMillis();
				will(returnValue(currentMillis));
				
				allowing(endpoint).getId();
				will(returnValue(swarmOwnerID));
			}
		});
	}

	@Test
	public void aSwarmIsDisabledByDefault() {
		assertFalse(swarm.isEnabled());
	}
	
	@Test
	public void enableShouldEnableTheSwarm() {
		context.checking(new Expectations() {
			{
				allowing(endpoint).route(with(any(Id.class)), with(any(Message.class)), with(any(NodeHandle.class)));
			}
		});
		swarm.enable();
		context.assertIsSatisfied();
		assertTrue(swarm.isEnabled());
	}
	
	@Test
	public void enableShouldSendAHeartBeatToSwarmParticipants() {
		context.checking(new Expectations() {
			{
				for (NodeHandle nh: participants) {
					one(endpoint).route(null, new HeartbeatMessage(swarmOwner, currentMillis, swarmOwnerID), nh);
				}
			}
		});
		swarm.enable();
		context.assertIsSatisfied();
		assertTrue(swarm.isEnabled());
	}
	
//	@Test
//	public void enableShouldSendAHeartBeatToSwarmParticipantsAndScheduleANewHeartbeat() {
//		context.checking(new Expectations() {
//			{
//				one(environment).getSelectorManager().schedule(task);
//				
//				for (NodeHandle nh: participants) {
//					one(endpoint).route(null, new HeartbeatMessage(swarmOwner, currentMillis, swarmOwnerID), nh);
//				}
//			}
//		});
//		swarm.enable();
//		context.assertIsSatisfied();
//		assertTrue(swarm.isEnabled());
//	}
}
